#include "../include/data_pool.h"
#include <stdlib.h>

#define DEBUG_DATA_POOL 1
#undef DEBUG_DATA_POOL

#ifdef DEBUG_DATA_POOL
#include <stdio.h>
#endif

struct DataPool
{
    DATA_TYPE *buf;
    size_t size;
    size_t stock;
    size_t head;
    size_t tail;
};

//static DataPool *dataPool = NULL;

static void *(*pMalloc)(size_t sz) = malloc;
static void (*pFree)(void *ptr) = free;

void poolInitMemoryHooks(MemoryHooks *hooks)
{
    if(!hooks) /* Reset hooks*/
    {
	pMalloc = malloc;
	pFree = free;
    }
    else
    {
	pMalloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
	pFree = (hooks->free_fn)?hooks->free_fn:free;
    }
}

DataPool *createDataPool(size_t size)
{
    int ret;
    DataPool * obj = NULL;

    obj = (DataPool *) pMalloc(sizeof(DataPool));

#ifdef DEBUG_DATA_POOL
    printf("[createDataPool] :sizeof(DataPool) is %d\n",sizeof(DataPool));
    fflush(stdout);
#endif	

    if(!obj)
    {
#ifdef DEBUG_DATA_POOL
	printf("[createDataPool] :create pool failed\n");
	fflush(stdout);
#endif	
	return NULL;
    }

    obj->buf = (DATA_TYPE*) pMalloc(size * sizeof(DATA_TYPE));

    if(!(obj->buf))
    {
#ifdef DEBUG_DATA_POOL
	printf("[createDataPool] :get melloc failed\n");
	fflush(stdout);
#endif
	return NULL;
    }

    obj->size = size;
    obj->stock = 0;
    obj->head = 0;
    obj->tail = 0;
    
    return obj;
}

int releaseDataPool(DataPool *obj)
{
    if(!obj || !(obj->buf))
    {
	return FAIL;
    }

    pFree(obj->buf);
    obj->buf = NULL;
    obj->size = 0;
    obj->stock = 0;
    obj->head = 0;
    obj->tail = 0;
    pFree(obj);
    obj = NULL;
    return 0;
}

static size_t updateRing(size_t len,size_t n)
{
    return ( (n + 1) == len ? 0 : (n + 1));
}

static int writeOneData(DataPool *obj, DATA_TYPE d)
{
    if( !obj || !(obj->buf))
    {
#ifdef DEBUG_DATA_POOL
	printf("[writeOneData] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

    if( (obj->stock) < (obj->size) )
    {
	obj->buf[obj->head] = d;
	obj->head = updateRing(obj->size,obj->head);
	obj->stock++;
	return 0;
    }
    else
    {
	return DATA_POOL_FULL;
    }
}

static int readOneData(DataPool *obj ,DATA_TYPE * const pdata)
{
    size_t pos;

    if( !obj || !(obj->buf) )
    {
#ifdef DEBUG_DATA_POOL
	printf("[readOneData] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

    if( obj->stock > 0)
    {
	pos = obj->tail;
	obj->tail = updateRing(obj->size,obj->tail);
	obj->stock--;
	*pdata = obj->buf[pos];
	return 0;
    }
    else
    {
	return DATA_POOL_EMPTY;
    }
}

int writeToDataPool(DataPool * const obj, DATA_TYPE *buf, size_t len)
{
    size_t count = 0;
    int ret = 0;

    if( !obj || !(obj->buf))
    {
#ifdef DEBUG_DATA_POOL
	printf("[writeToDataPool] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

#ifdef DEBUG_DATA_POOL
    printf("[writeToDataPool] : start:\n");
    printf("[writeToDataPool] : obj->size is %d\n",obj->size);
    printf("[writeToDataPool] : obj->stock is %d\n",obj->stock);
    printf("[writeToDataPool] : obj->head is %d\n",obj->head);
    printf("[writeToDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif

    for( count = 0; count < len; count++)
    {
	#ifdef DEBUG_DATA_POOL
	printf("[writeToDataPool] : count is %d\n",count);
	fflush(stdout);
	#endif
	ret = writeOneData(obj,buf[count]);
	if( ret != 0)
	{
	    len = count;
	    return ret;
	}
    }

#ifdef DEBUG_DATA_POOL
    printf("[writeToDataPool] : write done:\n");
    printf("[writeToDataPool] : obj->size is %d\n",obj->size);
    printf("[writeToDataPool] : obj->stock is %d\n",obj->stock);
    printf("[writeToDataPool] : obj->head is %d\n",obj->head);
    printf("[writeToDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif

    return 0;
}


int readFromDataPool(DataPool * const obj, DATA_TYPE *buf, size_t len)
{
    size_t count = 0;
    int ret = 0;

    if (!obj || !(obj->buf))
    {
#ifdef DEBUG_DATA_POOL
	printf("[readFromDataPool] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

#ifdef DEBUG_DATA_POOL
    printf("[readFromDataPool] : start：\n");
    printf("[readFromDataPool] : obj->size is %d\n",obj->size);
    printf("[readFromDataPool] : obj->stock is %d\n",obj->stock);
    printf("[readFromDataPool] : obj->head is %d\n",obj->head);
    printf("[readFromDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif

    for (count = 0; count < len; count++)
    {
	#ifdef DEBUG_DATA_POOL
	printf("[readFromDataPool] : count is %d\n",count);
	fflush(stdout);
	#endif
	ret = readOneData(obj, &buf[count]);
	if (ret != 0)
	{
	    len = count;
	    return len;
	}
    }

#ifdef DEBUG_DATA_POOL
    printf("[readFromDataPool] : read done:\n");
    printf("[readFromDataPool] : obj->size is %d\n",obj->size);
    printf("[readFromDataPool] : obj->stock is %d\n",obj->stock);
    printf("[readFromDataPool] : obj->head is %d\n",obj->head);
    printf("[readFromDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif
    return 0;
}
